import { Prefab, SpriteFrame, Node } from "cc";
import Singleton from "../Base/Singleton";
import {
  EntityTypeEnum,
  IActorMove,
  IClientInput,
  IRoom,
  IState,
  ITimePast,
  IWeaponAttack,
  InputTypeEnum,
  toFixed,
} from "../Common";
import { ActorManager } from "../Entity/Actor/ActorManager";
import { JoyStickManager } from "../UI/JoyStickManager";
import { BulletManager } from "../Entity/Bullet/BulletManager";
import EventManager from "./EventManager";
import { EventEnum } from "../Enum";
import { randomBySeed } from "../Utils";

const ACTOR_MOVE_SPEED: number = 100;
const BULLET_MOVE_SPEED: number = 600;
const SCREEN_WIDTH: number = 960;
const SCREEN_HEIGHT: number = 640;
const ACTOR_RADIUS: number = 50;
const BULLET_RADIUS: number = 10;
const BULLET_DAMAGE: number = 10;

export default class DataManager extends Singleton {
  static get Instance() {
    return super.GetInstance<DataManager>();
  }
  stage: Node;
  mapActor: Map<number, ActorManager> = new Map<number, ActorManager>();
  mapBullet: Map<number, BulletManager> = new Map<number, BulletManager>();

  mapPrefab: Map<string, Prefab> = new Map<string, Prefab>();
  mapTexture: Map<string, SpriteFrame[]> = new Map<string, SpriteFrame[]>();

  jm: JoyStickManager;
  myPlayerId: number = 1;
  roomInfo: IRoom;

  frameId: number = 0;
  lastState: IState;
  state: IState = {
    actors: [
      // {
      //   id: 1,
      //   hp: 30,
      //   type: EntityTypeEnum.Actor1,
      //   weaponType: EntityTypeEnum.Weapon1,
      //   bulletType: EntityTypeEnum.Bullet2,
      //   position: {
      //     x: -150,
      //     y: -150,
      //   },
      //   direction: {
      //     x: 0,
      //     y: 1,
      //   },
      // },
      // {
      //   id: 2,
      //   hp: 100,
      //   type: EntityTypeEnum.Actor1,
      //   weaponType: EntityTypeEnum.Weapon1,
      //   bulletType: EntityTypeEnum.Bullet2,
      //   position: {
      //     x: 150,
      //     y: 150,
      //   },
      //   direction: {
      //     x: -1,
      //     y: 0,
      //   },
      // },
    ],
    bullets: [],
    nextBulletId: 0,
    seed: 1,
  };

  applyInput(input: IClientInput) {
    switch (input.type) {
      case InputTypeEnum.ActorMove:
        this.applyMove(input);
        break;
      case InputTypeEnum.WeaponShoot:
        this.applyShoot(input);
        break;
      case InputTypeEnum.TimePast:
        this.applyTimePast(input);
        break;
      default:
        break;
    }
  }

  applyTimePast(input: ITimePast) {
    const { deltaTime } = input;
    const { bullets, actors } = this.state;

    for (let i = bullets.length - 1; i >= 0; i--) {
      let hitActor = false;
      for (let j = actors.length - 1; j >= 0; j--) {
        let dis = Math.sqrt(
          (actors[j].position.x - bullets[i].position.x) ** 2 +
            (actors[j].position.y - bullets[i].position.y) ** 2
        );
        if (dis < ACTOR_RADIUS + BULLET_RADIUS) {
          EventManager.Instance.emit(EventEnum.ExplosionBurn, bullets[i].id, {
            x: toFixed((bullets[i].position.x + actors[j].position.x) / 2),
            y: toFixed((actors[j].position.y + bullets[i].position.y) / 2),
          });

          const rand = randomBySeed(this.state.seed);
          this.state.seed = rand;
          const damage =
            rand / 233280 > 0.5 ? BULLET_DAMAGE * 2 : BULLET_DAMAGE;

          actors[j].hp -= damage;
          bullets.splice(i, 1);
          hitActor = true;
          break;
        }
      }
      if (hitActor) {
        break;
      }

      if (
        Math.abs(bullets[i].position.x) > SCREEN_WIDTH / 2 ||
        Math.abs(bullets[i].position.y) > SCREEN_HEIGHT / 2
      ) {
        EventManager.Instance.emit(EventEnum.ExplosionBurn, bullets[i].id, {
          x: bullets[i].position.x,
          y: bullets[i].position.y,
        });

        bullets.splice(i, 1);
        break;
      }
    }

    for (let thisbullet of bullets) {
      thisbullet.position.x += toFixed(
        deltaTime * BULLET_MOVE_SPEED * thisbullet.direction.x
      );
      thisbullet.position.y += toFixed(
        deltaTime * BULLET_MOVE_SPEED * thisbullet.direction.y
      );
    }
  }

  applyShoot(input: IWeaponAttack) {
    const { owner, position, direction, type } = input;

    let bullet = {
      id: this.state.nextBulletId++,
      type: this.mapActor.get(owner).bulletType,
      owner,
      position,
      direction,
    };

    EventManager.Instance.emit(EventEnum.BulletBurn, owner);

    this.state.bullets.push(bullet);

    // console.log(this.state.bullets);
  }

  applyMove(input: IActorMove) {
    const {
      id,
      direction: { x, y },
      deltaTime,
    } = input;

    let thisActor = this.state.actors.find((act) => act.id === id);

    if (thisActor) {
      thisActor.direction.x = x;
      thisActor.direction.y = y;

      thisActor.position.x += toFixed(x * ACTOR_MOVE_SPEED * deltaTime);
      thisActor.position.y += toFixed(y * ACTOR_MOVE_SPEED * deltaTime);
    }
  }
}
